/*
 * file:  expr.cpp (implementation of Expr).
 * Author: Jeremy Johnson 
 * Date: 2/5/07
 *
 * Description: 
 */

#include <iostream>
#include <string>
#include <map>
#include <list>
#include <cstdlib>
#include <sstream>
#include "programext.h"

using namespace std;

// RAMInstruction is one instruction in the RAM instruction set
// This could have 0 or 1 operands (HLT, for example, has no operands)
RAMInstruction::RAMInstruction(Operators oper, string operand, int lineNumber, Types type)
{
	lineNumber_ = lineNumber;
	oper_ = oper;
	operand_ = operand;
	type_ = type;
}

void RAMInstruction::setLinked(Operators oper, int operand) 
{
	linkedoper_ = oper;
	linkedoperand_ = operand;
}	

// Returns this instruction's line number
int RAMInstruction::getLineNumber() 
{
	return lineNumber_;
}

// Sets this instruction's line number
int RAMInstruction::setLineNumber(int LnNum) 
{
	lineNumber_ = LnNum;
	return lineNumber_;
}

// Returns this instruction's operator
Operators RAMInstruction::getOper() 
{
	return oper_;
}

// Returns this instruction's type
Types RAMInstruction::getType() 
{
	return type_;
}

// Returns this instruction's operator
Operators RAMInstruction::getLinkedOper() 
{
	return linkedoper_;
}

// Returns this instruction's operator
int RAMInstruction::getLinkedOperand() 
{
	return linkedoperand_;
}

// Sets the constant value for this instruction
void RAMInstruction::setConstantValue(string constantvalue) 
{
	constantvalue_ = constantvalue;
}

// Sets the constant value for this instruction
string RAMInstruction::getConstantValue() 
{
	return constantvalue_;
}

// Returns this instructions operator as a string
string RAMInstruction::getOperString() 
{
	string operatorString[19] = { "LD" , "ST", "ADD", "SUB" , "JMN" , "JMZ",
		"HLT", "LBL", "JMP", "MUL" , "LDI" , "LDA" , "STI", "STA" , "UNK",
		"JMI", "CAL", "RTN" };
	return operatorString[oper_];
}

// Returns this instructions operator as a string
string RAMInstruction::getLinkedOperString() 
{
	string operatorString[19] = { "LD" , "ST", "ADD", "SUB" , "JMN" , "JMZ",
		"HLT", "LBL", "JMP", "MUL" , "LDI" , "LDA" , "STI", "STA" , "UNK" ,
		"JMI" , "CAL", "RTN" };
	return operatorString[linkedoper_];
}

// Returns this instruction's operand
string RAMInstruction::getOperand() 
{
	return operand_;
}

// RAMCounter is a collection of counters to keep
// a running tally of used constants and temporary variables
// instructions_ is the list of instructions
// processed so far
RAMCounter::RAMCounter()
{
	list<RAMInstruction> instructions_;
	constant_ = 1;
	temporary_ = 1;
	label_ = 1;
	lineCounter_ = 1;
	CurrentFunc_="";
	constantTable_.clear();
	localTable_.clear();
	tempTable_.clear();
	labelTable_.clear();
	paramTable_.clear();
	ConstantValueTable_.clear();
	FuncTable_.clear();
	FtempTable_.clear();
	FlocalTable_.clear();

}

// Get the next available constant and then increment it
// If constant_ = 4, then this would return C4 and then 
// the next call would return C5, and so on.
string RAMCounter::getNextConstant(int value) 
{
	stringstream ss;
	ss << constant_;
	//cout << CurrentFunc_ << endl;
	string result = "C" + ss.str();

	ConstantValueTable_[result]=value;
	constant_++;
	return result;
}

// Get the next available temporary and then increment it
// If temporary_ = 4, then this would return T4 and then 
// the next call would return T5, and so on.
string RAMCounter::getNextTemporary() 
{
	stringstream ss;
	string result;
	ss << temporary_;
	if (CurrentFunc_=="main")
		result = "T" + ss.str();
	else
		result = CurrentFunc_ + "_T" + ss.str();

	temporary_++;
	return result;
}

// Get the next available parameter and then increment it
// If temporary_ = 4, then this would return T4 and then 
// the next call would return T5, and so on.
string RAMCounter::getNextParam(int value) 
{
	stringstream ss;
	ss << temporary_;
	
	string result = CurrentFunc_ + "_P" + ss.str();
	ParamValueTable_[result]=value;
	param_++;
	return result;
}
// Get the next available label and then increment it
// If label_ = 4, then this would return L4 and then 
// the next call would return L5, and so on.
string RAMCounter::getNextLabel() 
{
	stringstream ss;
	ss << label_;

	string result = "L" + ss.str();
	label_++;
	return result;
}

// Adds an instruction to RAMCounter
void RAMCounter::addInstruction(Operators oper, string operand, Types type) 
{
	RAMInstruction ri(oper, operand, this->lineCounter_, type);
	this->instructions_.push_back(ri);
	lineCounter_++;
} 
void RAMCounter::createActRecord(string name)
{
	map<string,int>::iterator p;
	map<string,int>::iterator l;
	map<string,int>::iterator t;
	int index = 0;
	for (p = FparamTable_.begin(); p != FparamTable_.end(); p++) 
	{
		offsetTable_[p->first]=index;
		offsetValueTable_[p->first]=index++;
	}
	for (l = FlocalTable_.begin(); l != FlocalTable_.end(); l++) 
	{
		offsetTable_[l->first]=index;
		offsetValueTable_[l->first]=index++;
	}
	for (t = FtempTable_.begin(); t != FtempTable_.end(); t++) 
	{
		offsetTable_[t->first]=index;
		offsetValueTable_[t->first]=index++;
	}
	actRecSizeTable_[name]=index+3;
	getActRecordSize(name);
}
// Get the next available parameter and then increment it
// If temporary_ = 4, then this would return T4 and then 
// the next call would return T5, and so on.
int RAMCounter::getActRecordSize(string name) 
{
	
	int temp=actRecSizeTable_.find(name)->second;
	//cout << "Rec size = " << temp << endl;
	return temp;
}
void RAMCounter::createActRecIndex(string name, string type)
{
	//cout << name << endl;
	if (type== "TEMP")
		FtempTable_[name]=ftemporary_++;
	else if (type == "LOCAL") 
		FlocalTable_[name]=flocal_++;
	else if (type == "PARAM")
		FparamTable_[name]=fparam_++;
	else
	{
		cout << "illegal type" << endl;
		exit(1);
	}
}

void RAMCounter::resetActRecIndex()
{
	FlocalTable_.clear();
	FtempTable_.clear();
	FparamTable_.clear();
	flocal_=1;
	ftemporary_=1;
	fparam_=1;
	actRecIndex_=1;
}
void RAMCounter::clearTables()
{
	localTable_.clear();
	tempTable_.clear();
	paramTable_.clear();

}
// Memory
// Dumps the content of the tables to help build the
// Memory file for input into RAM
void RAMCounter::memory()
{
	int sp_;
	map<string,int>::iterator mapiter;
	map<string,int>::iterator valiter = ConstantValueTable_.begin();
  	//cout << "Memory List " << endl;
  	sp_=constantTable_.size()+localTable_.size()+tempTable_.size()+offsetTable_.size();
	sp_++;
	for (mapiter = constantTable_.begin();mapiter != constantTable_.end();mapiter++)
	{
		if (mapiter->first=="SP")
			cout << mapiter->second << " " << sp_ << " ;" << mapiter->first << endl;
		else
		{
			valiter = ConstantValueTable_.find(mapiter->first);
			cout << mapiter->second << " " << valiter->second << " ;" << mapiter->first << endl;
		}
	}
  	for (mapiter = localTable_.begin();mapiter != localTable_.end();mapiter++)
    	cout << mapiter->second << " 0 ;" << mapiter->first << endl;
  	for (mapiter = tempTable_.begin();mapiter != tempTable_.end();mapiter++)
    	cout << mapiter->second << " 0 ;" << mapiter->first << endl;
  	valiter = offsetTable_.begin();
	for (mapiter = offsetTable_.begin();mapiter != offsetTable_.end();mapiter++)
	{
		valiter = offsetValueTable_.find(mapiter->first);
		cout << mapiter->second << " " << valiter->second << " ;" << mapiter->first << endl;
	}
}

// Linker
// Iterate over the instructions
// Assign, in order the CONSTANT variables, to memory locations
// Assign, in order the LOCAL variables, to memory locations
// Assign, in order the TEMP variables, to memory locations
// Remove the label markers
// Replace the JMN and JMZ labels to be the line number of the labels
// Assign the resulting operands to the linked property of RAM instruction
void RAMCounter::link()
{
	list<RAMInstruction>::iterator p;
	map<string,int>::iterator mapiter;
	int allCounter;
	offsetTable_.clear();
	allCounter = 1;
	// Go through all the instructions and create the map
	// of constants
	for (p = instructions_.begin();p != instructions_.end();p++) 
	{
		if(p->getType() == CONST) 
		{
			if(this->constantTable_.find(p->getOperand()) == constantTable_.end()) 
			{
				this->constantTable_[p->getOperand()] = allCounter;
				allCounter++;
			}	
		}
	}
	for (p = instructions_.begin();p != instructions_.end();p++) 
  	{
  		if(p->getType() == LOCAL) 
  		{
  			if(this->localTable_.find(p->getOperand()) == localTable_.end()) 
  			{
  				this->localTable_[p->getOperand()] = allCounter;
  				allCounter++;
  			}
  		}
  	}

	for (p = instructions_.begin();p != instructions_.end();p++) 
  	{
  		if(p->getType() == TEMP) 
  		{
  			if(this->tempTable_.find(p->getOperand()) == tempTable_.end()) 
  			{
  				this->tempTable_[p->getOperand()] = allCounter;
  				allCounter++;
  			}
  		}
  	}

	for (p = instructions_.begin();p != instructions_.end();p++) 
  	{
  		if(p->getType() == OFFSET) 
  		{
  			if(this->offsetTable_.find(p->getOperand()) == offsetTable_.end()) 
  			{
  				this->offsetTable_[p->getOperand()] = allCounter;
				//cout << "in link " << offsetTable_.first() << "," << offsetTable_.second() << endl;
				allCounter++;
  			}
  		}
  	}
	

	for (p = instructions_.begin();p != instructions_.end();p++) 
  	{
  		if(p->getOper() == LBL && p->getType() == LABEL) 
  		{
  			if(this->labelTable_.find(p->getOperand()) == labelTable_.end()) 
  			{
  				this->labelTable_[p->getOperand()] = p->getLineNumber();
  			}
  		}
  	}

	for (p=instructions_.begin(); p!=instructions_.end();p++)
	{
		if (p->getOper()==LBL && p->getType()==LABEL)
		{
			p=instructions_.erase(p);
		}
	}
	
	for (p = instructions_.begin();p != instructions_.end();p++) 
	{
		if(p->getType()==CONST) 
		{
			p->setLinked(p->getOper(),constantTable_[p->getOperand()]);
		}
		else if(p->getType()==OFFSET) 
		{
			p->setLinked(p->getOper(),offsetTable_[p->getOperand()]);
		}
		else if(p->getType()==LOCAL)
		{
			if(p->getOper()==LD)
				p->setLinked(LDA,localTable_[p->getOperand()]);
			else if(p->getOper()==ST)	
				p->setLinked(STA,localTable_[p->getOperand()]);
			else
				p->setLinked(p->getOper(),localTable_[p->getOperand()]);
		}
		else if( p->getType()==TEMP) 
		{
			if(p->getOper()==LD)
				p->setLinked(LDA,tempTable_[p->getOperand()]);
			else if(p->getOper()==ST)	
				p->setLinked(STA,tempTable_[p->getOperand()]);
			else
				p->setLinked(p->getOper(),tempTable_[p->getOperand()]);
		}
 		else if(p->getType()==LABEL)
		{
			p->setLinked(p->getOper(),labelTable_[p->getOperand()]);
		}	
 		else if(p->getType()==UNDEFINED)
		{
			p->setLinked(p->getOper(),0);
		}
 		else if(p->getOper()==HLT)
		{
			p->setLinked(p->getOper(),0);
		}	
 		else if(p->getOper()==CAL)
		{
			//cout << "made it" << endl;
			p->setLinked(p->getOper(),0);
		}	
 		else if(p->getOper()==RTN)
		{
			//cout << "made it" << endl;
			p->setLinked(p->getOper(),0);
		}	
	}		
}

void RAMCounter::dump() 
{
	list<RAMInstruction>::iterator p;

	for (p = instructions_.begin();p != instructions_.end();p++) 
	{
//		if(p->getOper()==HLT)
		if(p->getType()==UNDEFINED)
		{
			cout<< p->getLinkedOperString() << " ; "<< p->getLineNumber() << " " << p->getOperString() << " " << p->getOperand() << endl;
		}
		else 
		{
			cout<< p->getLinkedOperString() << " " << p->getLinkedOperand() << " ; "<< p->getLineNumber() << " " << p->getOperString() << " " << p->getOperand() << endl;
		}
	}
	
}

// Adds a label to RAMCounter
void RAMCounter::addLabel(string lbl) 
{
	RAMInstruction ri(LBL, lbl, this->lineCounter_, LABEL);
	this->instructions_.push_back(ri);
} 

// Peephole optimization to remove redundant LD statements
void RAMCounter::optimize() 
{
	list<RAMInstruction>::iterator p;
	for (p=instructions_.begin(),p++; p!=instructions_.end();p++) 
		if (p->getOperString()=="LD")
		{
			string op = p->getOperand();
			advance(p,-1);
			if (p->getOperString()=="ST" && p->getOperand()==op)
			{
				advance(p,1);
				p=instructions_.erase(p);
			}
			else
				advance(p,1);
		}

	int n=1;
	for (p=instructions_.begin(); p!=instructions_.end();p++)
	{
		p->setLineNumber(n);
		if (p->getOperString()!="LBL")
			n++;
	}
	//cout << endl;
} 
/*
map<string,int> RAMCounter::getTable(string s)
{
	switch(s)
	{
	case "constants":
		return constantTable_;
		break;
	case "local":
		return localTable_;
		
	}

}
*/

Program::Program(StmtList *SL, bool optimizer_)
{
	NameTable_.clear();
	FunctionTable_.clear();
	SL_ = SL;
	optimizer = optimizer_;
}

void Program::dump() 
{
      RAMCounter_.dump();
/*
	  map<string,int>::iterator p;
  map<string,Proc*>::iterator f;

  cout << "Dump of Symbol Table" << endl;
  cout << "Name Table" << endl;
  for (p = NameTable_.begin();p != NameTable_.end();p++)
    cout << p->first << " -> " << p->second << endl;
  cout << "Function Table" << endl;
  for (f = FunctionTable_.begin();f != FunctionTable_.end();f++) 
    cout << f->first << endl;
*/
}

void Program::eval() 
{
	SL_->eval(NameTable_, FunctionTable_);
}

// Compile method runs translate
// Optionally runs the optimizer
// Then runs the linker
void Program::compile()
{
	this->translate();
	if(optimizer==true)
		RAMCounter_.optimize();
	RAMCounter_.link();
}

void Program::memory()
{
	RAMCounter_.memory();
}

// Translate the program into RAL
// Example: a := 3, a :=2; b := 3, and so on
// Simply call the translate function for the list of statements
void Program::translate()
{
	RAMCounter_.addInstruction(JMP,"main",LABEL);
	SL_->translate(SymbolTable_ , RAMCounter_);
	RAMCounter_.addInstruction(HLT,"",UNDEFINED);
}

void StmtList::insert(Stmt * S)
{
  SL_.push_front(S);
}

void StmtList::eval(map<string,int> &NT, map<string,Proc*> &FT) 
{
  list<Stmt*>::iterator Sp;
  for (Sp = SL_.begin();Sp != SL_.end();Sp++)
	(*Sp)->eval(NT,FT);
}
// Translate this list of statements into RAL
// Examples: a := 3 ; if a then b:=3 else b:=2 fi; a := b - a ; a := a - 1, and so on
// stmt_list:  stmt ';' stmt_list |  stmt
// Iterate over the list and process each one
void StmtList::translate(map<string,int> &T, RAMCounter &rc) 
{
	//cout << "StmtList" << endl;
  	list<Stmt*>::iterator Sp;
  	for (Sp = SL_.begin();Sp != SL_.end();Sp++)
		(*Sp)->translate(T,rc);
}

AssignStmt::AssignStmt(string name, Expr *E)
{
  name_ = name;
  E_ = E;
}

void AssignStmt::eval(map<string,int> &NT, map<string,Proc*> &FT) const
{
	NT[name_] = E_->eval(NT,FT);
}

// Translate this assignment statement into RAL
// Examples: a := 3, a := b - a, a := a - 1, and so on
// assign_stmt: IDENT ASSIGNOP expr
// The value of expr is stored in temporary variable temp1
// Load temp1 into the accumulator
// Store the accumulator to the value at the identifier
void AssignStmt::translate(map<string,int> &t, RAMCounter &rc) const
{
	string ONE_, THREE_, temp1, temp2;
	temp1 = E_->translate( t , rc );
	//cout << name_ << endl;
	if (rc.CurrentFunc_ == "main")
	{
		rc.addInstruction(LDA, temp1,TEMP);
		rc.addInstruction(STA, name_, LOCAL);
	}
	else
	{
		temp2 = rc.CurrentFunc_ + "_" + name_;
		rc.addInstruction(LDA, "FP", CONST);
		rc.addInstruction(ADD, temp1, OFFSET);
		rc.createActRecIndex(temp1,"TEMP");
		rc.addInstruction(STA, "FPB", CONST);
		rc.addInstruction(LDI, "FPB", CONST);
		rc.addInstruction(STA, "SCRATCH", CONST);
		rc.addInstruction(LDA, "FP", CONST);
		rc.addInstruction(ADD, temp2, OFFSET);
		rc.createActRecIndex(temp2,"LOCAL");
		rc.addInstruction(STA, "FPB", CONST);
		rc.addInstruction(LDA, "SCRATCH", CONST);
		rc.addInstruction(STI, "FPB", CONST);
		if (name_=="return")
		{
			//cout << "yipee" << endl;
			rc.addInstruction(LDA, "SP", CONST);
			THREE_=rc.getNextConstant(3);
			rc.addInstruction(SUB, THREE_, CONST);
			rc.addInstruction(STA, "FPB", CONST);
			rc.addInstruction(LDA, "SCRATCH", CONST);
			rc.addInstruction(STI, "FPB", CONST);
			rc.addInstruction(LDA, "SP", CONST);
			ONE_=rc.getNextConstant(1);
			rc.addInstruction(SUB, ONE_, CONST);
			rc.addInstruction(STA, "FPB", CONST);
			rc.addInstruction(LDI, "FPB", CONST);
			rc.addInstruction(RTN, "", UNDEFINED);

		}
	}

}

DefineStmt::~DefineStmt()
{
  delete P_; 
}

DefineStmt::DefineStmt(string name, Proc *P)
{
  name_ = name;
  P_ = P;
}

void DefineStmt::eval(map<string,int> &NT, map<string,Proc*> &FT) const
{
	FT[name_] = P_;
}


void DefineStmt::translate(map<string,int> &t, RAMCounter &rc) const
{
	string label1;
	map<string,int> constant_;
	rc.CurrentFunc_=name_;
	rc.addLabel(name_);
	P_->translate(t,rc);
	rc.createActRecord(name_);
	rc.CurrentFunc_="";

}

IfStmt::IfStmt(Expr *E, StmtList *S1, StmtList *S2)
{
  E_ = E;
  S1_ = S1;
  S2_ = S2;
}

IfStmt::~IfStmt() { delete E_; delete S1_; delete S2_; }

void IfStmt::eval(map<string,int> &NT, map<string,Proc*> &FT) const
{
	if (E_->eval(NT,FT) > 0)
		S1_->eval(NT,FT);
	else
		S2_->eval(NT,FT);
}
// Translate this IF statement into RAL
// Examples: if b-a then a := b - a else a := a - b fi, and so on
// if_stmt: IF expr THEN stmt_list ELSE stmt_list FI
// Get the next available label into label1
// Set label1 at the point where the else statement list begins
// Get another label into label2
// Set label2 at the statement at the end of the else
// The value of expr is stored in temporary variable temp1
// Load temp1 into the accumulator
// If the accumulator is negative, jump to label1
// If the accumulator is positive, jump to label1
// Otherwise, evaluate the statements in the then condition
// After processing the then condition, jump to the label2
void IfStmt::translate(map<string,int> &T, RAMCounter &rc) const
{
	string temp1, label1, label2;	

	temp1 = E_->translate(T,rc);
	label1 = rc.getNextLabel();
	label2 = rc.getNextLabel();
	if (rc.CurrentFunc_=="main")
		rc.addInstruction(LDA, temp1, TEMP);
	else
	{
		rc.addInstruction(LDA, "FP", CONST);
		rc.addInstruction(ADD, temp1, OFFSET);
		rc.addInstruction(STA, "FPB", CONST);
		rc.addInstruction(LDI, "FPB", CONST);
	}

	rc.addInstruction(JMN, label1, LABEL);
	rc.addInstruction(JMZ, label1, LABEL);
	S1_->translate(T,rc);
	rc.addInstruction(JMP, label2, LABEL);
	rc.addLabel(label1);
	S2_->translate(T,rc);
	rc.addLabel(label2);
}
WhileStmt::WhileStmt(Expr *E, StmtList *S)
{
  E_ = E;
  S_ = S;
}

WhileStmt::~WhileStmt() { delete E_; delete S_; }

void WhileStmt::eval(map<string,int> &NT, map<string,Proc*> &FT) const
{
	while (E_->eval(NT,FT) > 0) 
		S_->eval(NT,FT);
}

// Translate this While statement into RAL
// Examples: while a do a := a -1 od, while b-c do b := b + 1; c := c - 1 od, and so on
// while_stmt : WHILE expr DO stmt_list OD
// Get the next available label into label1
// Set label1 at the point where expr is calculated
// Get another label into label2
// Set label2 at the statement at the end of the loop
// The value of expr is stored in temporary variable temp1
// Load temp1 into the accumulator
// If the accumulator is negative, jump to label2
// If the accumulator is positive, jump to label2
// Otherwise, evaluate the statements in the body of the loop
// Then jump back to the top of the loop
void WhileStmt::translate(map<string,int> &T, RAMCounter &rc) const
{
	string temp1, label1, label2;

	label1 = rc.getNextLabel();
	label2 = rc.getNextLabel();
	
	rc.addLabel(label1);
	temp1 = E_->translate(T,rc);
	if (rc.CurrentFunc_=="main")
		rc.addInstruction(LDA, temp1, TEMP);
	else
	{
		rc.addInstruction(LDA, "FP", CONST);
		rc.addInstruction(ADD, temp1, OFFSET);
		rc.createActRecIndex(temp1,"TEMP");
		rc.addInstruction(STA, "FPB", CONST);
		rc.addInstruction(LDI, "FPB", CONST);
	}
	rc.addInstruction(JMN, label2, LABEL);
	rc.addInstruction(JMZ, label2, LABEL);
	S_->translate(T,rc);
	rc.addInstruction(JMP, label1, LABEL);
	rc.addLabel(label2);
}

Number::Number(int value)
{
	value_ = value;
}

int Number::eval(map<string,int> NT, map<string,Proc*> FT) const
{
	//cout << "eval number " << value_ << endl; 
	return value_;
}

// Translate this number into RAL
// Examples: 17, 44, 23, 0, and so on
// factor (result) : IDENT (expr1)
// Expr1 is a constant
// Load expr1 into the accumulator
// Store the accumulator into temp1, the next available temp
// Return the name of temp1 to the calling program
string Number::translate(map<string,int> t, RAMCounter &rc) const
{

	string con, temp1;
	//cout << "number " << value_ << endl; 
	con = rc.getNextConstant(value_);
	temp1 = rc.getNextTemporary();
	if (rc.CurrentFunc_== "main")
	{
		rc.addInstruction(LDA, con, CONST);
		rc.addInstruction(STA, temp1, TEMP);
	}
	else
	{
		rc.addInstruction(LDA, "FP", CONST);
		rc.createActRecIndex(temp1,"TEMP");
		rc.addInstruction(ADD, temp1, OFFSET);
		rc.addInstruction(STA, "FPB", CONST);
		rc.addInstruction(LDA, con, CONST);
		rc.addInstruction(STI, "FPB", CONST);
	}
	return temp1;
}

Ident::Ident(string name)
{
	name_ = name;
}

int Ident::eval(map<string,int> NT, map<string,Proc*> FT) const
{
	return NT[name_];
}

// Translate this identifier into RAL
// Examples: A, B, price, result, and so on
// factor (result) : IDENT (expr1)
// Expr1 is not in a temporary variable
// Load expr1 into the accumulator
// Store the accumulator into temp1, the next available temp
// Return the name of temp1 to the calling program
string Ident::translate(map<string,int> t, RAMCounter &rc) const
{
	
	string temp1, temp2;
	temp1 = rc.getNextTemporary();
	if (rc.CurrentFunc_=="main")
	{
		rc.addInstruction(LDA, name_, LOCAL);
		rc.addInstruction(STA, temp1, TEMP);
	}
	else
	{
		temp2 = rc.CurrentFunc_ + "_" + name_;
		rc.addInstruction(LDA, "FP", CONST);
		rc.createActRecIndex(temp1,"TEMP");
		rc.addInstruction(ADD, temp1, OFFSET);
		rc.addInstruction(STA, "SCRATCH", CONST);
		rc.addInstruction(LDA, "FP", CONST);
		rc.addInstruction(ADD, temp2, OFFSET);
		rc.addInstruction(STA, "FPB", CONST);
		rc.addInstruction(LDA, "SCRATCH", CONST);
		rc.addInstruction(STI, "FPB", CONST);

	}

	return temp1;
}

Plus::Plus(Expr* op1, Expr* op2)
{
	op1_ = op1;
	op2_ = op2;
}

int Plus::eval(map<string,int> NT, map<string,Proc*> FT) const
{
	return op1_->eval(NT,FT) + op2_->eval(NT,FT);
}

// Translate this addition operation into RAL
// Examples: 3 + 5, 2 + a, i + j
// expr (result) : expr (expr1) + term (expr2)
// Temporary variable of expr1 is saved in temp1
// Temporary variable of expr2 is saved in temp2
// Load temp1 into the accumulator
// Apply operation from temp2 to the accumulator
// Store the accumulator into temp3, the next available temp
// Return the name of temp3 to the calling program
string Plus::translate(map<string,int> t , RAMCounter &rc ) const
{
	string temp1, temp2, temp3;

	temp1 = op1_->translate(t , rc);
	temp2 = op2_->translate(t , rc);
	temp3 = rc.getNextTemporary();
	if (rc.CurrentFunc_=="main")
	{
		rc.addInstruction(LDA, temp1, TEMP);
		rc.addInstruction(ADD, temp2, TEMP);
		rc.addInstruction(STA, temp3, TEMP);
	}
	else
	{
		rc.addInstruction(LDA, "FP", CONST);
		//calculate offset for temp1
		rc.addInstruction(ADD, temp1, OFFSET);
		rc.createActRecIndex(temp1,"TEMP");
		rc.addInstruction(STA, "FPB", CONST);
		rc.addInstruction(LDA, "FP", CONST);
		//calculate offset for temp2
		rc.addInstruction(ADD, temp2, OFFSET);
		rc.createActRecIndex(temp2,"TEMP");
		rc.addInstruction(STA, "SCRATCH", CONST);
		rc.addInstruction(LDI, "SCRATCH", CONST);
		rc.addInstruction(STA, "SCRATCH", CONST);
		rc.addInstruction(LDI, "FPB", CONST);
		rc.addInstruction(ADD, "SCRATCH", CONST);
		rc.addInstruction(STA, "SCRATCH", CONST);
		rc.addInstruction(LDA, "FP", CONST);
		// calculate offset for temp3
		rc.addInstruction(ADD, temp3, OFFSET);
		rc.createActRecIndex(temp3,"TEMP");
		rc.addInstruction(STA, "FPB", CONST);
		rc.addInstruction(LDA, "SCRATCH", CONST);
		rc.addInstruction(STI, "FPB", CONST);
	}

	return temp3;
}

Minus::Minus(Expr* op1, Expr* op2)
{
	op1_ = op1;
	op2_ = op2;
}

int Minus::eval(map<string,int> NT, map<string,Proc*> FT) const
{
	return op1_->eval(NT,FT) - op2_->eval(NT,FT);
}

// Translate this subtraction operation into RAL
// Examples: 3 - 2, 10 - k, after - before, and so on
// expr (result) : expr (expr1) - term (expr2)
// Temporary variable of expr1 is saved in temp1
// Temporary variable of expr2 is saved in temp2
// Load temp1 into the accumulator
// Apply operation from temp2 to the accumulator
// Store the accumulator into temp3, the next available temp
// Return the name of temp3 to the calling program
string Minus::translate(map<string,int> t , RAMCounter &rc ) const
{
	string temp1, temp2, temp3;

	//cout << "Minus" << endl;
	temp1 = op1_->translate(t , rc);
	temp2 = op2_->translate(t , rc);
	temp3 = rc.getNextTemporary();
	if (rc.CurrentFunc_=="main")
	{
		rc.addInstruction(LDA, temp1, TEMP);
		rc.addInstruction(SUB, temp2, TEMP);
		rc.addInstruction(STA, temp3, TEMP);
	}
	else
	{
		rc.addInstruction(LDA, "FP", CONST);
		//calculate offset for temp1
		rc.addInstruction(ADD, temp1, OFFSET);
		rc.createActRecIndex(temp1,"TEMP");
		rc.addInstruction(STA, "FPB", CONST);
		rc.addInstruction(LDA, "FP", CONST);
		//calculate offset for temp2
		rc.addInstruction(ADD, temp2, OFFSET);
		rc.createActRecIndex(temp2,"TEMP");
		rc.addInstruction(STA, "SCRATCH", CONST);
		rc.addInstruction(LDI, "SCRATCH", CONST);
		rc.addInstruction(STA, "SCRATCH", CONST);
		rc.addInstruction(LDI, "FPB", CONST);
		rc.addInstruction(SUB, "SCRATCH", CONST);
		rc.addInstruction(STA, "SCRATCH", CONST);
		rc.addInstruction(LDA, "FP", CONST);
		// calculate offset for temp3
		rc.addInstruction(ADD, temp3, OFFSET);
		rc.createActRecIndex(temp3,"TEMP");
		rc.addInstruction(STA, "FPB", CONST);
		rc.addInstruction(LDA, "SCRATCH", CONST);
		rc.addInstruction(STI, "FPB", CONST);
	}
	return temp3;
}

Times::Times(Expr* op1, Expr* op2)
{
	op1_ = op1;
	op2_ = op2;
}

int Times::eval(map<string,int> NT, map<string,Proc*> FT) const
{
	return op1_->eval(NT,FT) * op2_->eval(NT,FT);
}

// Translate this multiplation operation into RAL
// Examples: 4 * 2, r * 7, width * height
// term (result) : term (expr1) * factor (expr2)
// Temporary variable of expr1 is saved in temp1
// Temporary variable of expr2 is saved in temp2
// Load temp1 into the accumulator
// Apply operation from temp2 to the accumulator
// Store the accumulator into temp3, the next available temp
// Return the name of temp3 to the calling program
string Times::translate(map<string,int> t , RAMCounter &rc ) const
{
	string temp1, temp2, temp3;
	temp1 = op1_->translate(t , rc);
	temp2 = op2_->translate(t , rc);
	temp3 = rc.getNextTemporary();
	if (rc.CurrentFunc_=="main")
	{
		rc.addInstruction(LDA, temp1, TEMP);
		rc.addInstruction(MUL, temp2, TEMP);
		rc.addInstruction(STA, temp3, TEMP);
	}
	else
	{
		rc.addInstruction(LDA, "FP", CONST);
		//calculate offset for temp1
		rc.addInstruction(ADD, temp1, OFFSET);
		rc.createActRecIndex(temp1,"TEMP");
		rc.addInstruction(STA, "FPB", CONST);
		rc.addInstruction(LDA, "FP", CONST);
		//calculate offset for temp2
		rc.addInstruction(ADD, temp2, OFFSET);
		rc.createActRecIndex(temp2,"TEMP");
		rc.addInstruction(STA, "SCRATCH", CONST);
		rc.addInstruction(LDI, "SCRATCH", CONST);
		rc.addInstruction(STA, "SCRATCH", CONST);
		rc.addInstruction(LDI, "FPB", CONST);
		rc.addInstruction(MUL, "SCRATCH", CONST);
		rc.addInstruction(STA, "SCRATCH", CONST);
		rc.addInstruction(LDA, "FP", CONST);
		// calculate offset for temp3
		rc.addInstruction(ADD, temp3, OFFSET);
		rc.createActRecIndex(temp3,"TEMP");
		rc.addInstruction(STA, "FPB", CONST);
		rc.addInstruction(LDA, "SCRATCH", CONST);
		rc.addInstruction(STI, "FPB", CONST);
	}

	return temp3;
}

FunCall::FunCall(string name, list<Expr*> *AL)
{
	name_= name;
	AL_ = AL;
}

FunCall::~FunCall() { delete AL_; }

int FunCall::eval(map<string,int> NT, map<string,Proc*> FT) const
{
	//cout << "in funcall" << endl;
	return FT[name_]->apply(NT, FT, AL_);
}

string FunCall::translate(map<string,int> t , RAMCounter &rc ) const
{
	int recOffset;
	string ONE,TWO,THREE, FOUR, temp1, temp2, temp3;
	//cout << "Funcall Translate" << endl;
	
	// Update FP and SP
	
	//Save FP to Scratch
	rc.addInstruction(LDA, "FP", CONST);
	rc.addInstruction(STA, "SCRATCH", CONST);
	//update FP to SP
	rc.addInstruction(LDA, "SP", CONST);
	rc.addInstruction(STA, "FP", CONST);
	//Get size of activation record
	recOffset=rc.getActRecordSize(name_)+1;
	//cout << "record offset = " << recOffset << endl;
	temp1=rc.getNextConstant(recOffset);
	//cout << "THIS IS the act rec offset :" << recOffset << " " << temp1 << endl;
	ONE=rc.getNextConstant(1);
	TWO=rc.getNextConstant(2);
	//cout << "two is " << TWO << endl;
	THREE=rc.getNextConstant(3);
	//Update SP
	rc.addInstruction(ADD, temp1, CONST);
	rc.addInstruction(STA, "SP", CONST);
	// Store Previous FP
	rc.addInstruction(SUB, TWO, CONST);
	rc.addInstruction(STA, "FPB", CONST);
	rc.addInstruction(LDA, "SCRATCH", CONST);
	rc.addInstruction(STI, "FPB", CONST);
	
	
	//Store Return Address.  CAL instruction places PC in ACC	
	rc.addInstruction(LDA, "SP", CONST);
	//calculate offset for RETURN and Store return address
	rc.addInstruction(SUB, ONE, CONST);
	rc.addInstruction(STA, "FPB", CONST);
	rc.addInstruction(CAL, "", UNDEFINED);	
	FOUR=rc.getNextConstant(4);
	rc.addInstruction(ADD, FOUR, CONST);
	rc.addInstruction(STI, "FPB", CONST);

	// Jump to the begining of function	
	rc.addInstruction(JMP, name_, LABEL);

	//Retrieve return value and store in temp3
	rc.addInstruction(LDA, "SP", CONST);
	//calculate offset for RETURN and Store return address
	rc.addInstruction(SUB, THREE, CONST);
	rc.addInstruction(STA, "FPB", CONST);
	rc.addInstruction(LDI, "FPB", CONST);
	temp3=rc.getNextTemporary();
	rc.addInstruction(STA, temp3, TEMP);

	//Retrieve Previous FP value and store in scratch
	rc.addInstruction(LDA, "SP", CONST);
	//calculate offset for RETURN and Store return address
	rc.addInstruction(SUB, TWO, CONST);
	rc.addInstruction(STA, "FPB", CONST);
	rc.addInstruction(LDI, "FPB", CONST);
	rc.addInstruction(STA, "SCRATCH", CONST);

	//Restore SP
	rc.addInstruction(LDA, "FP", CONST);
	rc.addInstruction(STA, "SP", CONST);
	//Restore FP
	rc.addInstruction(LDA, "SCRATCH", CONST);
	rc.addInstruction(STA, "FP", CONST);
//	temp3 = "";
	
	return temp3;
}


Proc::Proc(list<string> *PL, StmtList *SL)
{
	SL_ = SL;
	PL_ = PL;
	NumParam_ = PL->size();
}

int Proc::apply(map<string,int> &NT, map<string,Proc*> &FT, list<Expr*> *EL) 
{
	map<string,int> NNT;
	NNT.clear();
	//cout << "in Proc::apply" << endl;
	// bind parameters in new name table

	list<string>::iterator p;
	list<Expr*>::iterator e;
	if (NumParam_ != EL->size()) {
		//cout << "Param count does not match" << endl;
		exit(1);
	}
	for (p = PL_->begin(), e = EL->begin(); p != PL_->end(); p++, e++) 
		NNT[*p] = (*e)->eval(NT,FT);

	// evaluate function body using new name table and old function table

	SL_->eval(NNT,FT);
	if ( NNT.find("return") != NNT.end() )
		return NNT["return"];
	else {
		//cout << "Error:  no return value" << endl;
		exit(1);
	}
}
void Proc::translate(map<string,int> &t, RAMCounter &rc)
{
	string temp1;
	rc.resetActRecIndex();
	list<string>::iterator p;
	list<Expr*>::iterator e;
	if (rc.CurrentFunc_!="main")
	{
		for (p = PL_->begin(); p != PL_->end(); p++) 
		{
		
			temp1=rc.getNextParam(0);
			rc.addInstruction(LDA, "FP", CONST);
			//calculate offset for temp1
			rc.addInstruction(ADD, temp1, OFFSET);
			rc.createActRecIndex(temp1,"PARAM");
			rc.addInstruction(STA, "FPB", CONST);

			rc.addInstruction(LDA, *p, LOCAL);
			//rc.addInstruction(STA, rc.CurrentFunc_ + "_" + *p, LOCAL);
			rc.addInstruction(STI, "FPB", CONST);
		}

	}
	SL_->translate(t,rc);
}